package com.xxx.netty.protocol.custom.protocol;

import com.xxx.netty.protocol.custom.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.List;

/**
 * 自定义协议消息编码和解码器
    相当于两个handler合二为一，既能入站 也能做出站处理
      <b>魔数      </b>，用来在第一时间判定是否是无效数据包
      <b>版本号    </b>，可以支持协议的升级
      <b>序列化算法 </b>，消息正文到底采用哪种序列化反序列化方式，可以由此扩展，例如：json、protobuf、hessian、jdk
      <b>指令类型  </b>，是登录、注册、单聊、群聊... 跟业务相关
      <b>请求序号  </b>，为了双工通信，提供异步能力
      <b>正文长度  </b>
      <b>消息正文  </b>
 */
@Slf4j
public class MessageCodec extends ByteToMessageCodec<Message> {

    // 编码器
    @Override
    public void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {
        // 1. 4个字节的魔数
        out.writeBytes(new byte[]{1, 2, 3, 4});

        // 2. 1个字节的版本号
        out.writeByte(1);

        // 3. 1个字节的序列化算法类型：0-jdk，1-json，2-protobuf，3-hessian
        out.writeByte(0);  // 这里用jdk的序列化方式

        // 4. 1个字节的指令类型：Message类中定义的消息类型列表
        out.writeByte(msg.getMessageType());

        // 5. 4个字节的请求序号
        out.writeInt(msg.getSequenceId());

        // 无意义的填充：由于除去消息正文的字段的长度之和为15（4+1+1+1+4+4），为了对其长度为2的n次方，需要填充一些无意义的字符
        out.writeByte(0xff);  // 这里填充1个字节的0xff,2的整数倍

        // 6. 消息正文长度
        // 先获取消息内容的字节数组
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(msg);
        byte[] bytes = bos.toByteArray();
        int len = bytes.length;
        // 4个字节的消息正文长度
        out.writeInt(len);

        // 7. 消息正文(byte数组)
        out.writeBytes(bytes);
    }

    // 解码器
    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 解码过程为编码过程的逆过程
        // 1. 4个字节魔数
        int magicNum = in.readInt();  // readInt()表示读取4个字节的整数（read*类型的方法会移动读指针，get*类型的方法则不会）

        // 2. 1个字节的版本号
        byte versionNo = in.readByte();

        // 3. 1个字节的序列化算法类型：0-jdk，1-json，2-protobuf，3-hessian
        byte serializableType = in.readByte();

        // 4. 1个字节的指令类型：Message类中定义的消息类型列表
        byte messageType = in.readByte();

        // 5. 4个字节的请求序号
        int sequenceId = in.readInt();

        // 1个字节的无意义的填充
        in.readByte();

        // 6. 消息正文长度
        int len = in.readInt();

        // 7. 消息正文(byte数组)
        // 读取范围为[0, len]的内容到byte数组里，作为正文的内容
        byte[] bytes = new byte[len];
        in.readBytes(bytes, 0, len);
        // 将byte[]还原为Message对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        Message msg = (Message) ois.readObject();

        log.info("magicNum: {}, versionNo: {}, serializableType: {}, messageType: {}, sequenceId: {}", magicNum, versionNo, serializableType, messageType, sequenceId);
        log.info("msg: {}", msg);

        // 将msg对象添加到out列表中
        out.add(msg);
    }

}
